package top.cardone.data.jdbc.datasource.looku.impl;

import com.google.common.collect.Maps;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import top.cardone.cache.Cache;
import top.cardone.context.ApplicationContextHolder;
import top.cardone.context.event.EventAspect;
import top.cardone.context.util.MapUtils;
import top.cardone.context.util.TableUtils;
import top.cardone.data.jdbc.datasource.looku.RoutingDataSource;
import top.cardone.data.jdbc.datasource.looku.RoutingDataSourceAspect;

import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @author yao hai tao
 */
@lombok.extern.log4j.Log4j2
public class RoutingDataSourceAspectImpl extends EventAspect implements RoutingDataSourceAspect {
    @Setter
    @Getter
    protected Map<String, List<String>> config = Maps.newHashMap();

    @Setter
    private String taskExecutorBeanName = "slowTaskExecutor";

    @Setter
    private Long countUpperLimit = 1000L;

    private String getDataSourceKey(String className) {
        if (MapUtils.isEmpty(config)) {
            return "default";
        }

        return ApplicationContextHolder.getBean(Cache.class).get(RoutingDataSourceAspectImpl.class.getName(), 1, className,
                () -> StringUtils.defaultIfBlank(top.cardone.context.util.StringUtils.getPathForMatch(config, className), "default"));
    }

    @Override
    public Object postProcessBeforeSetTargetDataSource(ProceedingJoinPoint joinPoint) throws Throwable {
        if (Objects.nonNull(RoutingDataSource.KEY_THREAD_LOCAL.get())) {
            log(joinPoint);

            return super.eventAspect(joinPoint);
        }

        String targetName = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName();

        RoutingDataSource.KEY_THREAD_LOCAL.set(this.getDataSourceKey(targetName));

        if (log.isDebugEnabled()) {
            log.debug("使用数据源:" + RoutingDataSource.KEY_THREAD_LOCAL.get() + "," + targetName + "()");
        }

        try {
            return super.eventAspect(joinPoint);
        } finally {
            RoutingDataSource.KEY_THREAD_LOCAL.remove();
        }
    }

    private void log(ProceedingJoinPoint joinPoint) {
        ApplicationContextHolder.getBean(AsyncListenableTaskExecutor.class, this.taskExecutorBeanName).submitListenable(() -> {
            String targetName = null;

            if (log.isDebugEnabled()) {
                targetName = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName();

                log.debug("使用数据源:" + RoutingDataSource.KEY_THREAD_LOCAL.get() + "," + targetName + "()");
            }

            if (countUpperLimit > 0L) {
                if (StringUtils.isBlank(targetName)) {
                    targetName = joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName();
                }

                String rowKey = this.getClass().getName();

                Long count = TableUtils.longAdderIncrementGetSum(rowKey, targetName);

                if (count % countUpperLimit == 0) {
                    log.error(StringUtils.join("调用较频繁, rowKey: ", rowKey, ", columnKey: ", targetName, ", count: ", count));
                }
            }
        });
    }
}